// Enesea Data File Worker
// (c) jimon game studio

#ifndef JEH_JEJED
#define JEH_JEJED

#include "jeUnknown.h"
#include "jeStl.h"
#include "IBuffer.h"
#include "ILogManager.h"
#include <memory.h>

namespace je
{
	namespace core
	{
		//! JED Data Blocks Types
		enum JEDDataBlocksTypes
		{
			//! Null
			JEDDBT_NULL = 0,

			//! Offset Table
			JEDDBT_OFFSET_TABLE = 1,

			//! Mesh
			JEDDBT_MESH = 100,

			//! Surface
			JEDDBT_SURFACE = 101,

			//! Bitmap Font
			JEDDBT_BITMAPFONT = 102
		};

		//! Data Block
		class jeJED_DataBlock:public je::jeUnknown
		{
		public:
			//! Childrens List Type
			typedef jeList<jeJED_DataBlock*> ChildrensListType;

			//! Data Blocks Factory
			typedef jeFactory<jeJED_DataBlock,u16> DataBlockFactoryType;

		protected:
			u16 Id;
			u16 Name_Lenght;
			jeStringw Name;
			u32 DataSize;
			u8 Type;
			u32 Reserved;
			u32 Childrens_Size;
			u32 Childrens_Count;

			ChildrensListType Childrens;
			jeJED_DataBlock * Parent;
			DataBlockFactoryType * Factory;
		public:
			//! Destructor
			jeJED_DataBlock(u16 NewId)
				:Id(NewId),DataSize(0),Type(0),Reserved(0),Childrens_Size(0),Childrens_Count(0),Parent(NULL),Factory(NULL)
			{
			}

			//! Destructor
			virtual ~jeJED_DataBlock()
			{
				for(ChildrensListType::Iterator Iter = Childrens.begin();Iter != Childrens.end();Iter++)
					(*Iter)->Drop();
				Childrens.clear();
			}

			//! Get Id
			u16 GetId()
			{
				return Id;
			}

			//! Get Type
			u8 GetType()
			{
				return Type;
			}

			//! Get Name
			const jeStringw & GetName()
			{
				return Name;
			}

			//! Set Name
			void SetName(const jeStringw & NewName)
			{
				Name = NewName;
			}

			//! Load Data
			virtual void LoadData(core::IBuffer * Buffer)
			{
			}

			//! Load
			void Load(core::IBuffer * Buffer)
			{
				Buffer->Read(&Name_Lenght,2);

				if(Name_Lenght > 0)
				{
					je_wchar_t * Temp = NULL;
					JENEW_A(Temp,je_wchar_t,Name_Lenght+1)
					memset(Temp,0,(Name_Lenght+1)*2);
					Buffer->Read(Temp,Name_Lenght*2);
					Name = Temp;
					JEDELETE_A(Temp)
				}
				else
					Name = "";

				Buffer->Read(&DataSize,4);
				Buffer->Read(&Type,1);
				Buffer->Read(&Reserved,4);

				LoadData(Buffer);

				Buffer->Read(&Childrens_Size,4);
				Buffer->Read(&Childrens_Count,4);

				for(u32 i=0;i<Childrens_Count;i++)
				{
					u16 ChildrenId = 0;
					Buffer->Read(&ChildrenId,2);
					jeJED_DataBlock * ChildrenBlock = Factory->Create(ChildrenId);
					ChildrenBlock->SetFactory(Factory);
					ChildrenBlock->Load(Buffer);
					ChildrenBlock->Parent = this;
					Childrens.push_back(ChildrenBlock);
				}
			}

			//! Save Data
			virtual void SaveData(core::IBuffer * Buffer)
			{
			}

			//! Save
			void Save(core::IBuffer * Buffer)
			{
				Buffer->Write(&Id,2);
				Buffer->Write(&Name_Lenght,2);
				if(Name_Lenght>0)
					Buffer->Write(Name.c_str(),Name_Lenght*2);
				Buffer->Write(&DataSize,4);
				Buffer->Write(&Type,1);
				Buffer->Write(&Reserved,4);
				SaveData(Buffer);
				Buffer->Write(&Childrens_Size,4);
				Buffer->Write(&Childrens_Count,4);
				for(ChildrensListType::Iterator Iter = Childrens.begin();Iter != Childrens.end();Iter++)
					(*Iter)->Save(Buffer);
			}

			//! Add Children
			void AddChildren(jeJED_DataBlock * Children)
			{
				Children->Grab();
				Childrens.push_back(Children);
				Children->SetParent(this);
			}

			//! Remove Children
			void RemoveChildren(jeJED_DataBlock * Children)
			{
				for(ChildrensListType::Iterator Iter = Childrens.begin();Iter != Childrens.end();Iter++)
					if((*Iter) == Children)
					{
						Childrens.erase(Iter);
						Children->SetParent(NULL);
						Children->Drop();
						return;
					}
			}

			//! Remove Itself
			void RemoveItself()
			{
				if(Parent)
					Parent->RemoveChildren(this);
				else
					this->Drop();
			}

			//! Get Childrens
			ChildrensListType & GetChildrens()
			{
				return Childrens;
			}

			//! Set Parent
			void SetParent(jeJED_DataBlock * NewParent)
			{
				Parent = NewParent;
			}

			//! Get Parent
			jeJED_DataBlock * GetParent()
			{
				return Parent;
			}

			//! Get Factory
			DataBlockFactoryType * GetFactory()
			{
				return Factory;
			}

			//! Set Factory
			void SetFactory(DataBlockFactoryType * NewFactory)
			{
				Factory = NewFactory;
			}

			//! Get Block Size
			u32 GetBlockSize()
			{
				return 2 + 1 + Name_Lenght * 2 + 4 + 1 + 4 + DataSize + 4 + 4 + Childrens_Size;
			}

			//! Get Childrens Size
			u32 GetChildrensSize()
			{
				return Childrens_Size;
			}

			//! Prepare For Save
			virtual void PrepareForSave()
			{
				Name_Lenght = Name.GetSize();
				Childrens_Count = Childrens.getSize();
				Childrens_Size = 0;
				for(ChildrensListType::Iterator Iter = Childrens.begin();Iter != Childrens.end();Iter++)
				{
					(*Iter)->PrepareForSave();
					Childrens_Size+=(*Iter)->GetBlockSize();
				}
			}

			//! Get Full Path
			virtual jeStringw GetFullPath()
			{
				if(Parent)
					return Parent->GetFullPath()+jeStringw(".")+Name;
				else
					return Name;
			}

		};

		//! Null Block
		class jeJED_NullBlock:public je::core::jeJED_DataBlock
		{
		public:
			//! Constructor
			jeJED_NullBlock()
				:jeJED_DataBlock(JEDDBT_NULL)
			{
			}

			//! Destructor
			~jeJED_NullBlock()
			{
			}
		};

		//! Null Block
		class jeJED_OffsetTableBlock:public je::core::jeJED_DataBlock
		{
		protected:
			struct jeJED_OffsetPath
			{
				jeStringw Path;
				u32 Offset;
			};

			typedef jeList<jeJED_OffsetPath*> OffsetsListType;
			OffsetsListType Offsets;

			u32 TempOffset;

			void PushOffsets(jeJED_DataBlock * DataBlock,jeStringw Path)
			{
				jeJED_OffsetPath * OffsetPath = NULL;
				JENEW(OffsetPath,jeJED_OffsetPath)

				if(Path.GetSize()>0)
					OffsetPath->Path = Path+jeStringw(".")+DataBlock->GetName();
				else
					OffsetPath->Path = DataBlock->GetName();

				OffsetPath->Offset = TempOffset;

				Offsets.push_back(OffsetPath);

				TempOffset+=DataBlock->GetBlockSize()-DataBlock->GetChildrensSize();

				for(ChildrensListType::Iterator Iter = DataBlock->GetChildrens().begin();Iter != DataBlock->GetChildrens().end();Iter++)
					PushOffsets((*Iter),OffsetPath->Path);
			}
		public:
			//! Constructor
			jeJED_OffsetTableBlock()
				:jeJED_DataBlock(JEDDBT_OFFSET_TABLE)
			{
			}

			//! Destructor
			~jeJED_OffsetTableBlock()
			{
				for(OffsetsListType::Iterator Iter = Offsets.begin();Iter != Offsets.end();Iter++)
					JEDELETE(*Iter)
				Offsets.clear();
			}

			//! Load
			void LoadData(core::IBuffer * Buffer)
			{
				u32 PathsCount = 0;
				Buffer->Read(&PathsCount,4);

				for(u32 i = 0;i < PathsCount;i++)
				{
					jeJED_OffsetPath * Offset = NULL;
					JENEW(Offset,jeJED_OffsetPath)

					u16 PathSize = 0;
					Buffer->Read(&PathSize,2);

					if(PathSize > 0)
					{
						je_wchar_t * Temp = NULL;
						JENEW_A(Temp,je_wchar_t,PathSize+1)
						memset(Temp,0,(PathSize+1)*2);
						Buffer->Read(Temp,PathSize*2);
						Offset->Path = Temp;
						JEDELETE_A(Temp)
					}
					else
						Offset->Path = "";

					Buffer->Read(&Offset->Offset,4);

					Offsets.push_back(Offset);
				}
			}

			//! Save
			void SaveData(core::IBuffer * Buffer)
			{
				u32 PathsCount = Offsets.getSize();
				Buffer->Write(&PathsCount,4);
				for(OffsetsListType::Iterator Iter = Offsets.begin();Iter != Offsets.end();Iter++)
				{
					u16 PathSize = (*Iter)->Path.GetSize();
					Buffer->Write(&PathSize,2);
					Buffer->Write((*Iter)->Path.c_str(),PathSize*2);
					Buffer->Write(&(*Iter)->Offset,4);
				}
			}

			//! Prepare For Save
			void PrepareForSave()
			{
				DataSize = 4;
				for(OffsetsListType::Iterator Iter = Offsets.begin();Iter != Offsets.end();Iter++)
				{
					DataSize += 2 + 4;
					DataSize += (*Iter)->Path.GetSize()*2;
				}
				jeJED_DataBlock::PrepareForSave();
			}

			//! Calculate Offsets, Root Block must be prepared for saving
			void CalculateOffsets(jeJED_DataBlock * RootBlock,u32 HeaderOffset)
			{
				TempOffset = 0;
				PushOffsets(RootBlock,"");
				PrepareForSave();

				u32 Offset = GetBlockSize()+HeaderOffset;
				for(OffsetsListType::Iterator Iter = Offsets.begin();Iter != Offsets.end();Iter++)
					(*Iter)->Offset += Offset;
			}


			//! Get Offset
			u32 GetOffset(const jeStringw & Path)
			{
				for(OffsetsListType::Iterator Iter = Offsets.begin();Iter != Offsets.end();Iter++)
					if((*Iter)->Path == Path)
						return (*Iter)->Offset;
				return 0;
			}
		};

		//! Mesh Block
		class jeJED_MeshBlock:public je::core::jeJED_DataBlock
		{
		protected:
			jeStringw MeshName;
		public:
			//! Constructor
			jeJED_MeshBlock()
				:jeJED_DataBlock(JEDDBT_MESH)
			{
			}

			//! Destructor
			~jeJED_MeshBlock()
			{
			}

			//! Get Mesh Name
			const jeStringw & GetMeshName()
			{
				return MeshName;
			}

			//! Set Mesh Name
			void SetMeshName(const jeStringw & NewMeshName)
			{
				MeshName = NewMeshName;
			}

			//! Load
			void LoadData(core::IBuffer * Buffer)
			{
				u16 MeshNameSize = 0;
				Buffer->Read(&MeshNameSize,2);

				if(MeshNameSize > 0)
				{
					je_wchar_t * Temp = NULL;
					JENEW_A(Temp,je_wchar_t,MeshNameSize+1)
					memset(Temp,0,(MeshNameSize+1)*2);
					Buffer->Read(Temp,MeshNameSize*2);
					MeshName = Temp;
					JEDELETE_A(Temp)
				}
				else
					MeshName = "";
			}

			//! Save
			void SaveData(core::IBuffer * Buffer)
			{
				u16 MeshNameSize = MeshName.GetSize();
				Buffer->Write(&MeshNameSize,2);
				Buffer->Write(MeshName.c_str(),MeshNameSize*2);
			}

			//! Prepare For Save
			void PrepareForSave()
			{
				DataSize = 2+MeshName.GetSize()*2;
				jeJED_DataBlock::PrepareForSave();
			}
		};

		//! Surface Block
		class jeJED_SurfaceBlock:public je::core::jeJED_DataBlock
		{
		public:
			enum Draw_Operation_Type_List
			{
				DOTL_TRIANGLES=1,
				DOTL_TRIANGLE_STRIP=2,
				DOTL_LINES=3,
				DOTL_POINTS=4
			};
			struct DataBlock
			{
				u8 Id;
				u32 Block_Size;
				u8 * Data;

				DataBlock()
					:Id(0),Block_Size(0),Data(NULL)
				{
				}
				~DataBlock()
				{
					if(Data)
						JEDELETE_A(Data)
				}
			};
			typedef jeList<DataBlock*> DataBlockListType;

			jeStringw MaterialName;
			Draw_Operation_Type_List Draw_Operation_Type;
			DataBlockListType DataList;

		protected:
			void Clear()
			{
				MaterialName = "";
				Draw_Operation_Type = DOTL_TRIANGLES;
				for(DataBlockListType::Iterator Iter = DataList.begin();Iter!=DataList.end();Iter++)
					JEDELETE(*Iter)
				DataList.clear();
			}
		public:
			//! Constructor
			jeJED_SurfaceBlock()
				:jeJED_DataBlock(JEDDBT_SURFACE)
			{
				Clear();
			}

			//! Destructor
			~jeJED_SurfaceBlock()
			{
				Clear();
			}

			//! Get Material Name
			const jeStringw & GetMaterialName()
			{
				return MaterialName;
			}

			//! Set Material Name
			void SetMaterialName(const jeStringw & NewMaterialName)
			{
				MaterialName = NewMaterialName;
			}

			//! Load
			void LoadData(core::IBuffer * Buffer)
			{
				Clear();

				u16 MaterialNameSize = 0;
				Buffer->Read(&MaterialNameSize,2);

				if(MaterialNameSize > 0)
				{
					je_wchar_t * Temp = NULL;
					JENEW_A(Temp,je_wchar_t,MaterialNameSize+1)
					memset(Temp,0,(MaterialNameSize+1)*2);
					Buffer->Read(Temp,MaterialNameSize*2);
					MaterialName = Temp;
					JEDELETE_A(Temp)
				}
				else
					MaterialName = "";

				Buffer->Read(&Draw_Operation_Type,1);

				u32 BlocksCount = 0;
				Buffer->Read(&BlocksCount,1);
				for(u32 i=0;i<BlocksCount;i++)
				{
					DataBlock * AddBlock = NULL;
					JENEW(AddBlock,DataBlock)
					Buffer->Read(&AddBlock->Id,1);
					Buffer->Read(&AddBlock->Block_Size,4);
					JENEW_A(AddBlock->Data,u8,AddBlock->Block_Size)
					Buffer->Read(AddBlock->Data,AddBlock->Block_Size);
					DataList.push_back(AddBlock);
				}
			}

			//! Save
			void SaveData(core::IBuffer * Buffer)
			{
				u16 MaterialNameSize = MaterialName.GetSize();
				Buffer->Write(&MaterialNameSize,2);
				Buffer->Write(MaterialName.c_str(),MaterialNameSize*2);

				Buffer->Write(&Draw_Operation_Type,1);

				u32 BlocksCount = DataList.getSize();
				Buffer->Write(&BlocksCount,1);

				for(DataBlockListType::Iterator Iter = DataList.begin();Iter!=DataList.end();Iter++)
				{
					Buffer->Write(&(*Iter)->Id,1);
					Buffer->Write(&(*Iter)->Block_Size,4);
					Buffer->Write((*Iter)->Data,(*Iter)->Block_Size);
				}
			}

			//! Prepare For Save
			void PrepareForSave()
			{
				DataSize = 2+MaterialName.GetSize()*2;
				for(DataBlockListType::Iterator Iter = DataList.begin();Iter!=DataList.end();Iter++)
					DataSize += 1+4+(*Iter)->Block_Size;
				jeJED_DataBlock::PrepareForSave();
			}
		};

		//! Bitmap Font Block
		class jeJED_BitmapFontBlock:public je::core::jeJED_DataBlock
		{
		public:
			struct FontChar
			{
				je_wchar_t Code;
				f32 U1,V1,U2,V2,ShiftX,ShiftY,SizeX,SizeY,CharShift;
			};
			typedef jeList<FontChar*> FontCharListType;
			struct FontKerning
			{
				je_wchar_t Code1,Code2;
				f32 X,Y;
			};
			typedef jeList<FontKerning*> FontKerningListType;

		protected:
			jeStringw FontImageName;
			f32 FontSize;
			FontCharListType FontCharList;
			FontKerningListType FontKerningList;

		public:
			//! Constructor
			jeJED_BitmapFontBlock()
				:jeJED_DataBlock(JEDDBT_BITMAPFONT)
			{
				FontImageName = "";
				FontSize = 0;
			}

			//! Destructor
			~jeJED_BitmapFontBlock()
			{
				for(FontCharListType::Iterator Iter = FontCharList.begin();Iter!=FontCharList.end();Iter++)
					JEDELETE(*Iter)
				FontCharList.clear();
				for(FontKerningListType::Iterator Iter = FontKerningList.begin();Iter!=FontKerningList.end();Iter++)
					JEDELETE(*Iter)
				FontKerningList.clear();
			}

			//! Set Font Image Name
			void SetFontImageName(jeStringw NewName)
			{
				FontImageName = NewName;
			}

			//! Get Font Image Name
			jeStringw GetFontImageName()
			{
				return FontImageName;
			}

			//! Set Font Size
			void SetFontSize(f32 NewSize)
			{
				FontSize = NewSize;
			}

			//! Get Font Size
			f32 GetFontSize()
			{
				return FontSize;
			}

			//! Get Chars List
			FontCharListType & GetCharsList()
			{
				return FontCharList;
			}

			//! Get Kerning List
			FontKerningListType & GetKerningList()
			{
				return FontKerningList;
			}

			//! Load
			void LoadData(core::IBuffer * Buffer)
			{
				for(FontCharListType::Iterator Iter = FontCharList.begin();Iter!=FontCharList.end();Iter++)
					JEDELETE(*Iter)
				FontCharList.clear();

				u16 FontImageNameSize = 0;
				Buffer->Read(&FontImageNameSize,2);

				if(FontImageNameSize > 0)
				{
					je_wchar_t * Temp = NULL;
					JENEW_A(Temp,je_wchar_t,FontImageNameSize+1)
					memset(Temp,0,(FontImageNameSize+1)*2);
					Buffer->Read(Temp,FontImageNameSize*2);
					FontImageName = Temp;
					JEDELETE_A(Temp)
				}
				else
					FontImageName = "";

				Buffer->Read(&FontSize,4);

				u32 CharCount = 0;
				Buffer->Read(&CharCount,4);

				for(u32 i = 0;i<CharCount;i++)
				{
					FontChar * TempChar = NULL;
					JENEW(TempChar,FontChar)

					Buffer->Read(&TempChar->Code,2);
					Buffer->Read(&TempChar->U1,4);
					Buffer->Read(&TempChar->V1,4);
					Buffer->Read(&TempChar->U2,4);
					Buffer->Read(&TempChar->V2,4);
					Buffer->Read(&TempChar->ShiftX,4);
					Buffer->Read(&TempChar->ShiftY,4);
					Buffer->Read(&TempChar->SizeX,4);
					Buffer->Read(&TempChar->SizeY,4);
					Buffer->Read(&TempChar->CharShift,4);

					FontCharList.push_back(TempChar);
				}

				u32 KerningCount = 0;
				Buffer->Read(&KerningCount,4);

				for(u32 i = 0;i<KerningCount;i++)
				{
					FontKerning * TempKerning = NULL;
					JENEW(TempKerning,FontKerning)

					Buffer->Read(&TempKerning->Code1,2);
					Buffer->Read(&TempKerning->Code2,2);
					Buffer->Read(&TempKerning->X,4);
					Buffer->Read(&TempKerning->Y,4);

					FontKerningList.push_back(TempKerning);
				}
			}

			//! Save
			void SaveData(core::IBuffer * Buffer)
			{
				u16 FontImageNameSize = FontImageName.GetSize();
				Buffer->Write(&FontImageNameSize,2);
				Buffer->Write(FontImageName.c_str(),FontImageNameSize*2);

				Buffer->Write(&FontSize,4);

				u32 CharCount = FontCharList.getSize();
				Buffer->Write(&CharCount,4);

				for(FontCharListType::Iterator Iter = FontCharList.begin();Iter!=FontCharList.end();Iter++)
				{
					Buffer->Write(&(*Iter)->Code,2);
					Buffer->Write(&(*Iter)->U1,4);
					Buffer->Write(&(*Iter)->V1,4);
					Buffer->Write(&(*Iter)->U2,4);
					Buffer->Write(&(*Iter)->V2,4);
					Buffer->Write(&(*Iter)->ShiftX,4);
					Buffer->Write(&(*Iter)->ShiftY,4);
					Buffer->Write(&(*Iter)->SizeX,4);
					Buffer->Write(&(*Iter)->SizeY,4);
					Buffer->Write(&(*Iter)->CharShift,4);
				}

				u32 KerningCount = FontKerningList.getSize();
				Buffer->Write(&KerningCount,4);

				for(FontKerningListType::Iterator Iter = FontKerningList.begin();Iter!=FontKerningList.end();Iter++)
				{
					Buffer->Write(&(*Iter)->Code1,2);
					Buffer->Write(&(*Iter)->Code2,2);
					Buffer->Write(&(*Iter)->X,4);
					Buffer->Write(&(*Iter)->Y,4);
				}
			}

			//! Prepare For Save
			void PrepareForSave()
			{
				DataSize = 2+FontImageName.GetSize()*2+4+4+FontCharList.getSize()*sizeof(FontChar)+4+FontKerningList.getSize()*sizeof(FontKerning);
				jeJED_DataBlock::PrepareForSave();
			}
		};
	}
}

#endif
